#include "Qt3DPicker.h"

#include <Geom2d_Curve.hxx>
#include <ChFi2d_FilletAPI.hxx>
#include <gp_Pln.hxx>
#include <gp_Circ.hxx>
#include <BRep_Builder.hxx>
#include <TopExp.hxx>
#include <TopoDS.hxx>
#include <BRep_Tool.hxx>
#include <Geom_Line.hxx>
#include <Geom2d_Line.hxx>
#include <Geom_Circle.hxx>
#include <GeomAPI.hxx>
#include <Geom2d_TrimmedCurve.hxx>
#include <Geom2dAPI_InterCurveCurve.hxx>
#include <GCE2d_MakeSegment.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <Geom2dAPI_ProjectPointOnCurve.hxx>

#include "Fillet2DHandler.h"
#include "CmdManager.h"
#include "Scene.h"
#include "SendPointHandlerBase.h"
#include "SelectHandler3D.h"
#include "OSGNodeMask.h"
#include "Operation/OpAdd.h"
#include "Operation/OpReplace.h"
#include "LineStrip2D.h"
#include "Arc2D.h"
#include "Spline2D.h"
#include "QtOSGWidget.h"
#include "EntFromEdge.h"
#include "Arc2.h"
#include "OsgUtil.h"
#include "OcctOsg.h"
#include "OcctShapeProp.h"

bool Fillet2DHandler::s_trim = true;
double Fillet2DHandler::s_radius = 0.;

Fillet2DHandler::Fillet2DHandler()
{
}

Fillet2DHandler::~Fillet2DHandler()
{
}

void Fillet2DHandler::input(const QVariant& data, const QString& rawInput, CommandSource cs)
{
	auto scene = getScene();
	auto sph = getSendPointHandler();
	auto selector = qobject_cast<SelectHandler3D*>(getSelectHandler());
	if (data.type() == QVariant::Bool)
	{
		bool esc = !data.toBool();
		if (esc)
		{
			if(m_ent1)
				m_ent1->setSelected(false);
			selector->clearSelection();
			clearInput();
			emit finished();
			return;
		}
	}

	if (status() == 0)
	{
		if (selector)
		{
			selector->clearSelection();
			selector->beginSelect(D2LineMask | D2CurveMask, 1);
			prompt(1, tr("选择第一个对象或[半径(R)/修剪(T)]:"));
		}
		else
		{
			emit finished();
		}
		return;
	}


	if (status() == 1)
	{
		if (rawInput == "R" || rawInput == "r")
		{
			prompt(2, tr("指定圆角半径:"));
		}
		else if (rawInput == "T" || rawInput == "t")
		{
			prompt(3, tr("输入修剪模式选项[修剪(T)/不修剪(N)]:"));
		}
		else
		{
			if (selector->done())
			{
				std::set<std::shared_ptr<INode>> nodes = selector->selectionSet();
				if (nodes.empty())
				{
					emit finished();
					return;
				}
				std::shared_ptr<INode> node = *nodes.begin();
				m_ent1 = std::dynamic_pointer_cast<Ent2DCurve>(node);
				auto name = m_ent1->metaObject()->className();
				auto parent = m_ent1->getParent();
				UCS* ucs = dynamic_cast<UCS*>(parent);
				if (ucs)
				{
					m_ucs = ucs;
					bool success = getChoosePt(m_choosePt1);
					if (!success)
						return;
				}
				else
				{
					remark(tr("请选择二维图形"));
					emit finished();
					return;
				}
				selector->clearSelection();
				selector->beginSelect(D2LineMask | D2CurveMask, 1);
				prompt(4, tr("选择第二个对象:"));
				m_ent1->setSelected(true);
			}
		}
		return;
	}

	if (status() == 2)
	{
		if (data.type() == QVariant::Double)
		{
			s_radius = data.value<double>();
			prompt(1, tr("选择第一个对象或[半径(R)/修剪(T)]:"));
		}
		return;
	}

	if (status() == 3)
	{
		if (rawInput == "T" || rawInput == "t")
		{
			s_trim = true;
			prompt(1, tr("选择第一个对象或[半径(R)/修剪(T)]:"));
		}
		else  if (rawInput == "N" || rawInput == "n")
		{
			s_trim = false;
			prompt(1, tr("选择第一个对象或[半径(R)/修剪(T)]:"));
		}
		return;
	}

	if (status() == 4)
	{
		if (selector->done())
		{
			m_ent1->setSelected(false);
			auto selections = selector->selectionSet();
			if (selections.size() == 1)
			{
				auto selection = *selections.begin();
				m_ent2 = std::dynamic_pointer_cast<Ent2DCurve>(selection);
				auto parent = m_ent2->getParent();
				UCS* ucs = dynamic_cast<UCS*>(parent);
				if (ucs && ucs == m_ucs)
				{
					bool success = getChoosePt(m_choosePt2);
					if (!success)
						return;				
					ChFi2d_FilletAPI algo;
					gp_Pln plane = gp_Pln({ m_ucs->origin().x(),m_ucs->origin().y(), m_ucs->origin().z() },
						{ m_ucs->zAxis().x(),m_ucs->zAxis().y(), m_ucs->zAxis().z() });
					TopoDS_Edge e1, e2;
					success = getEdge(plane, e1, e2);
					if (!success)
					{
						selector->clearSelection();
						emit finished();
						return;
					}
					if (e1.IsNull() || e2.IsNull())
					{
						remark(tr("边不可为空"));
						selector->clearSelection();
						emit finished();
						return;
					}

					algo.Init(e1, e2, plane);
					Standard_Boolean status = algo.Perform(s_radius);
					if (!status)
					{
						remark(tr("半径错误"));
						selector->clearSelection();
						emit finished();
						return;
					}
					//gp_Pnt common = findCommonPoint(e1, e2);
					gp_Pnt commonPt((m_choosePt1.x() + m_choosePt2.x()) / 2, (m_choosePt1.y() + m_choosePt2.y()) / 2, 0);
					//Standard_Integer nbSolutions = algo.NbResults(commonPt);
					//if (!nbSolutions)
					//{
					//	remark(tr("无法圆角"));
					//	selector->clearSelection();
					//	emit finished();
					//	return;
					//}
					TopoDS_Edge M1, M2;
					TopoDS_Edge fillet = algo.Result(commonPt, M1, M2);
					if (fillet.IsNull())
					{
						remark(tr("无法圆角"));
						selector->clearSelection();
						emit finished();
						return;
					}
					std::shared_ptr<Ent2D> filletArc = makeEnt2D(fillet, m_ucs);
					filletArc->setColor(ucs->getUCSColor());
					// 记录下本次操作				
					auto undoStack = scene->undoStack();
					if (s_trim)
					{
						std::vector<std::shared_ptr<Ent>> origins;
						std::vector<std::shared_ptr<Ent>> replaces;
						std::shared_ptr<Ent2DCurve> ent1,ent2;
						auto arc = std::dynamic_pointer_cast<Arc2D>(filletArc);
						//std::shared_ptr<Ent2D> ent1 = makeEnt2D(M1, m_ucs);
						//std::shared_ptr<Ent2D> ent2 = makeEnt2D(M2, m_ucs);
						bool success1 = trimEnt(arc, m_ent1, m_segNum1, ent1);
						bool success2 = trimEnt(arc, m_ent2, m_segNum2, ent2);
						if (!(success1 && success2))
						{
							selector->clearSelection();
							emit finished();
							return;
						}
						origins.push_back(m_ent1);
						origins.push_back(m_ent2);
						replaces.push_back(ent1);
						replaces.push_back(ent2);
						replaces.push_back(filletArc);
						ent1->setColor(ucs->getUCSColor());
						ent2->setColor(ucs->getUCSColor());
						undoStack->push(new OpReplace(origins, replaces));
					}
					else
					{
						std::set<std::shared_ptr<INode>> children;
						children.insert(filletArc);
						undoStack->push(new OpAdd(children));
					}
					selector->clearSelection();
					emit finished();
					return;
				}
				else
				{
					remark(tr("直线不在同一平面内"));
					emit finished();
				}	
			}
		}
		return;
	}
}

TopoDS_Edge getLineEdge(osg::Vec3d startPt, osg::Vec3d endPt,osg::Vec3d interPt,osg::Vec3d choosePt)
{
	osg::Vec3d lineDir = endPt - startPt;
	osg::Vec3d interToStartDir = startPt - interPt;
	osg::Vec3d interToEndDir = endPt - interPt;
	osg::Vec3d interToChooseDir = choosePt - interPt;
	if (interToStartDir * interToEndDir > 0)
	{
		if (interToStartDir * lineDir > 0)
		{
			startPt = interPt;
		}
		else
		{
			endPt = interPt;
		}
	}
	else
	{
		if (interToChooseDir * interToStartDir > 0 && interToStartDir.length() > 0.01)
		{
			endPt = interPt;
		}
		else
		{
			if (interToEndDir.length() > 0.01)
				startPt = interPt;
		}
	}
	BRepBuilderAPI_MakeEdge edge(toPnt(startPt), toPnt(endPt));
	return edge;
}

bool Fillet2DHandler::getEdge(const gp_Pln& plane, TopoDS_Edge& e1, TopoDS_Edge& e2)
{
	Handle(Geom2d_Curve) curve1, curve2;
	auto curves1 = m_ent1->getCurve2d();
	auto curves2 = m_ent2->getCurve2d();
	m_segNum1 = getSegmentNo(m_ent1, m_choosePt1);
	m_segNum2 = getSegmentNo(m_ent2, m_choosePt2);
	if (m_ent1 == m_ent2 && m_segNum1 == m_segNum2)
	{
		remark(tr(" 图元无法用自身圆角"));
		return false;
	}
	curve1 = curves1[m_segNum1];
	curve2 = curves2[m_segNum2];
	if (m_ent1->metaObject() == &LineStrip2D::staticMetaObject &&
		m_ent2->metaObject() == &LineStrip2D::staticMetaObject)
	{
		std::shared_ptr<LineStrip2D> line1 = std::static_pointer_cast<LineStrip2D>(m_ent1);
		auto points1 = line1->getPoints();
		osg::Vec3d line1Dir = points1[m_segNum1 + 1] - points1[m_segNum1];	

		std::shared_ptr<LineStrip2D> line2 = std::static_pointer_cast<LineStrip2D>(m_ent2);
		auto points2 = line2->getPoints();
		osg::Vec3d line2Dir = points2[m_segNum2 + 1] - points2[m_segNum2];
		//求交
		Geom2dAPI_InterCurveCurve inter;
		inter.Init(basisCurve(curve1), basisCurve(curve2));
		if (inter.NbPoints() > 0)
		{
			auto pt = inter.Point(1);
			//两条直线的交点
			m_interPt = { double(pt.X()),double(pt.Y()),0 };
			e1 = getLineEdge(points1[m_segNum1], points1[m_segNum1 + 1], m_interPt,m_choosePt1);
			e2 = getLineEdge(points2[m_segNum2], points2[m_segNum2 + 1], m_interPt, m_choosePt2);
		}
		else
		{
			remark(tr("直线平行"));
			return false;
		}	
	}
	else
	{
		Handle(Geom_Curve) C1, C2;
		if (m_ent1->metaObject() == &Arc2D::staticMetaObject)
			C1 = GeomAPI::To3d(basisCurve(curve1), plane);
		else
			C1 = GeomAPI::To3d(curve1, plane);
		if (m_ent2->metaObject() == &Arc2D::staticMetaObject)
			C2 = GeomAPI::To3d(basisCurve(curve2), plane);
		else
			C2 = GeomAPI::To3d(curve2, plane);
		BRep_Builder BB;
		BB.MakeEdge(e1, C1, Precision::Confusion());
		BB.MakeEdge(e2, C2, Precision::Confusion());
	}
	return true;
}

int Fillet2DHandler::getSegmentNo(const std::shared_ptr<Ent2DCurve>& ent, const osg::Vec3d & choosePt)
{
	auto l1 = ent->getCurve2d();
	gp_Pnt2d ucsPnt1(choosePt.x(), choosePt.y());
	SectionParam hitPointInfo1;
	bool success = getSectionParam(l1, ucsPnt1, hitPointInfo1);
	if (!success)
	{
		remark(tr("请重新选择对象"));
		return -1;
	}
	return hitPointInfo1.sectionNo;
}

bool Fillet2DHandler::getChoosePt(osg::Vec3d& pt)
{
	auto widget3D = getWidget();
	osg::Vec2d cursor = widget3D->getCursorPoint();
	osg::Camera* camera = widget3D->getCamera();
	if (!camera)
	{
		emit finished();
		return false;
	}
	osg::Matrix VPW, invVPW;
	bool success = getCameraViewProjectionWindowMatrix(camera, VPW, invVPW);
	if (!success)
	{
		emit finished();
		return false;
	}
	osg::Vec3d worldPt(cursor.x(), cursor.y(), 0);
	worldPt = worldPt * invVPW;
	osg::Vec3d ucsPt;
	getViewPointOnPlane(*m_ucs, worldPt, ucsPt);
	pt = ucsPt;
	return true;
}

void Fillet2DHandler::trimPolyline(const std::shared_ptr<Ent2DCurve>& originEnt, const int segNum, std::shared_ptr<Ent2DCurve>& trimEnt)
{
	auto line = std::dynamic_pointer_cast<LineStrip2D>(trimEnt);
	auto originLine1 = std::dynamic_pointer_cast<LineStrip2D>(originEnt);
	auto points = line->getPoints();
	auto originPoints1 = originLine1->getPoints();
	if (points[0] == originPoints1[segNum + 1] || points[1] == originPoints1[segNum + 1])
	{
		for (int i = segNum + 2; i < originPoints1.size(); i++)
		{
			points.push_back(originPoints1[i]);
		}
	}
	else
	{
		auto pt = points[1];
		points.clear();
		for (int i = 0; i < segNum + 1; i++)
		{
			points.push_back(originPoints1[i]);
		}
		points.push_back(pt);
	}
	line->setPoints(points);
	return;
}

osg::Vec3d getPointOfAngle(const double angle, const osg::Vec3d& center, const double radius);

bool Fillet2DHandler::trimLine(const std::shared_ptr<Arc2D>& fillet, const std::shared_ptr<Ent2DCurve>& originEnt, const int segNum, std::shared_ptr<Ent2DCurve>& trimEnt)
{
	auto line = std::dynamic_pointer_cast<LineStrip2D>(originEnt);
	auto points = line->getPoints();
	auto startPt = points[segNum];
	auto endPt = points[segNum + 1];
	auto filletStartPt = getPointOfAngle(fillet->startAngle(), fillet->center(), fillet->radius());
	auto filletMidPoint = getPointOfAngle((fillet->startAngle() + fillet->endAngle()) / 2, fillet->center(), fillet->radius());
	auto filletEndPt = getPointOfAngle(fillet->endAngle(), fillet->center(), fillet->radius());
	double dis1 = (filletStartPt - startPt).length() + (filletStartPt - endPt).length();
	double dis2 = (filletEndPt - startPt).length() + (filletEndPt - endPt).length();
	osg::Vec3d  filletInterPt,filletVec;
	if (dis1 < dis2)
	{
		filletInterPt = filletStartPt;
		filletVec = filletMidPoint - filletStartPt;
	}
	else
	{
		filletInterPt = filletEndPt;
		filletVec = filletMidPoint - filletEndPt;
	}
	auto filletInterToStartVec = startPt - filletInterPt;
	auto filletInterToEndVec = endPt - filletInterPt;
	auto filletInterToFilletVec = m_interPt - filletInterPt;
	//判断半径是否太大
	if (filletInterToStartVec * filletInterToEndVec > 0 && 
		filletInterToStartVec * filletInterToFilletVec > 0)
	{
		remark(tr("半径太大"));
		return false;
	}
	auto lineVec = endPt - startPt;
	//判断需要裁剪的点
	if (lineVec * filletVec < 0)
	{
		startPt = filletInterPt;
	}
	else
	{
		endPt = filletInterPt;
	}
	std::vector<osg::Vec3d> trimPoints;
	trimPoints.push_back(startPt);
	trimPoints.push_back(endPt);
	trimEnt = std::make_shared<LineStrip2D>(m_ucs);
	auto trimLine = std::dynamic_pointer_cast<LineStrip2D>(trimEnt);
	trimLine->setPoints(trimPoints);
	if (points.size() > 2)
	{
		trimPolyline(originEnt, segNum, trimEnt);
	}
	return true;
}

bool Fillet2DHandler::trimArc(const std::shared_ptr<Arc2D>& fillet, const std::shared_ptr<Ent2DCurve>& originEnt, std::shared_ptr<Ent2DCurve>& trimEnt)
{
	auto arc = std::dynamic_pointer_cast<Arc2D>(originEnt);
	auto center = arc->center();
	auto arcStartPt = getPointOfAngle(arc->startAngle(), arc->center(), arc->radius());
	auto arcEndPt = getPointOfAngle(arc->endAngle(), arc->center(), arc->radius());
	auto filletStartPt = getPointOfAngle(fillet->startAngle(), fillet->center(), fillet->radius());
	auto filletMidPoint = getPointOfAngle((fillet->startAngle() + fillet->endAngle()) / 2, fillet->center(), fillet->radius());
	auto filletEndPt = getPointOfAngle(fillet->endAngle(), fillet->center(), fillet->radius());
	double dis1 = (filletStartPt - center).length();
	double dis2 = (filletEndPt - center).length();
	osg::Vec3d  filletInterPt, filletVec;
	//判断哪个点是交点
	if (dis1 < dis2)
	{
		filletInterPt = filletStartPt;
		filletVec = filletMidPoint - filletStartPt;
	}
	else
	{
		filletInterPt = filletEndPt;
		filletVec = filletMidPoint - filletEndPt;
	}
	auto centerToFilletPtVec = filletInterPt - center;
	osg::Vec3d tangentVec(-centerToFilletPtVec.y(), centerToFilletPtVec.x(), 0);
	trimEnt = std::make_shared<Arc2D>(m_ucs);
	auto trimArc = std::dynamic_pointer_cast<Arc2D>(trimEnt);
	//通过方向判断需要切割的部分
	if (filletVec * tangentVec > 0)
	{
		CArc2 arc2({ arcStartPt.x(),arcStartPt.y(),0 }, { center.x(),center.y(),0 }, { filletInterPt.x(),filletInterPt.y(),0 }, 0);
		trimArc->set(osg::Vec3d(arc2.center.x, arc2.center.y, 0), arc2.radius, arc2.StartAngle(), arc2.EndAngle());
	}
	else
	{
		CArc2 arc2({ filletInterPt.x(),filletInterPt.y(),0 }, { center.x(),center.y(),0 }, { arcEndPt.x(),arcEndPt.y(),0 }, 0);
		trimArc->set(osg::Vec3d(arc2.center.x, arc2.center.y, 0), arc2.radius, arc2.StartAngle(), arc2.EndAngle());
	}
	return true;
}

bool Fillet2DHandler::trimSpline(const std::shared_ptr<Arc2D>& fillet, const std::shared_ptr<Ent2DCurve>& originEnt, const int segNum, std::shared_ptr<Ent2DCurve>& trimEnt)
{
	auto spline = std::dynamic_pointer_cast<Spline2D>(originEnt);
	auto curve = spline->getCurve2d();
	auto filletStartPt = getPointOfAngle(fillet->startAngle(), fillet->center(), fillet->radius());
	auto filletMidPoint = getPointOfAngle((fillet->startAngle() + fillet->endAngle()) / 2, fillet->center(), fillet->radius());
	auto filletEndPt = getPointOfAngle(fillet->endAngle(), fillet->center(), fillet->radius());
	gp_Pnt2d startPnt(filletStartPt.x(), filletStartPt.y());
	gp_Pnt2d endPnt(filletEndPt.x(), filletEndPt.y());
	double startDis, endDis;
	Geom2dAPI_ProjectPointOnCurve proj;
	proj.Init(startPnt, curve[segNum]);
	if (proj.NbPoints() > 0)
	{
		startDis = proj.LowerDistance();
	}
	proj.Init(endPnt, curve[segNum]);
	if (proj.NbPoints() > 0)
	{
		endDis = proj.LowerDistance();
	}
	SectionParam from;
	SectionParam start, end;
	getStartEndParam(curve, start, end);
	bool success;
	osg::Vec3d filletVec;
	//判断哪个点是交点
	if (startDis < endDis)
	{	
		success = getSectionParam(curve, startPnt, from);
		filletVec = filletMidPoint - filletStartPt;
	}
	else
	{
		success = getSectionParam(curve, endPnt, from);
		filletVec = filletMidPoint - filletEndPt;
	}
	if (!success)
		return false;
	//得到交点的切线向量
	auto vec = curve[segNum]->DN(from.param, 1);
	osg::Vec3d splineVec = { vec.X(),vec.Y(),0 };
	std::shared_ptr<Ent> ent;
	//通过向量方向判断要切割的部分
	if (splineVec * filletVec < 0)
	{
		ent = spline->trim(start, from)[0];
	}
	else
	{
		ent = spline->trim(from, end)[0];
	}
	auto ent2d = std::dynamic_pointer_cast<Spline2D>(ent);
	trimEnt = ent2d;
	return true;
}

bool Fillet2DHandler::trimEnt(const std::shared_ptr<Arc2D>& fillet, const std::shared_ptr<Ent2DCurve>& originEnt, const int segNum, std::shared_ptr<Ent2DCurve>& trimEnt)
{
	bool success = false;
	if (originEnt->metaObject() == &LineStrip2D::staticMetaObject)
	{
		success = trimLine(fillet, originEnt, segNum, trimEnt);
	}
	else if (originEnt->metaObject() == &Arc2D::staticMetaObject)
	{
		success = trimArc(fillet, originEnt, trimEnt);	
	}
	else if (originEnt->metaObject() == &Spline2D::staticMetaObject)
	{
		success = trimSpline(fillet, originEnt, segNum, trimEnt);
	}
	return success;
}
